Google App Engine Recipe

Google App Engine (GAE) provides a range of hosting options from pure PaaS (App Engine Classic) through Managed VMs to fully self-managed or container-driven Compute Engine instances. Echo works great with all of these but requires a few changes to the usual examples to run on the AppEngine Classic and Managed VM options. With a small amount of effort though it’s possible to produce a codebase that will run on these and also non-managed platforms automatically.

We’ll walk through the changes needed to support each option.

Standalone

Wait? What? I thought this was about AppEngine! Bear with me - the easiest way to show the changes required is to start with a setup for standalone and work from there plus there’s no reason we wouldn’t want to retain the ability to run our app anywhere, right?

We take advantage of the gobuild constraints or tagsto change how we create and run the Echo server for each platform while keeping the rest of the application (e.g. handler wireup) the same across all of them.

First, we have the normal setup based on the examples but we split it into two files -app.gowill be common to all variations and holds the Echo instance variable. We initialise it from a function and because it is avarthis will happen_before_anyinit()functions run - a feature that we’ll use to connect our handlers later.

app.go

✖ Error 404 while fetching file: Not Found

A separate source file contains the function to create the Echo instance and add the static file handlers and middleware. Note the build tag on the first line which says to use this when_not_bulding with appengine or appenginevm tags (which thoese platforms automatically add for us). We also have themain()function to start serving our app as normal. This should all be very familiar.

app-standalone.go

✖ Error 404 while fetching file: Not Found

The handler-wireup that would normally also be a part of this Echo setup moves to separate files which take advantage of the ability to have multipleinit()functions which run_after_theeEcho var is initialised but_before_themain()function is executed. These allow additional handlers to attach themselves to the instance - I’ve found theGroupfeature naturally fits into this pattern with a file per REST endpoint, often with a higher-levelapigroup created that they attach to instead of the root Echo instance directly (so things like CORS middleware can be added at this higher common-level).

users.go

✖ Error 404 while fetching file: Not Found

If we run our app it should execute as it did before when everything was in one file although we have at least gained the ability to organize our handlers a little more cleanly.

AppEngine Classic and Managed VMs

So far we’ve seen how to split apart the Echo creation and setup but still have the same app that still only runs standalone. Now we’ll see hwo those changes allow us to add support for AppEngine hosting.

Refer to theAppEngine sitefor full configuration and deployment information.

app.yaml configuration file

Both of these are Platform as as Service options running on either sandboxed micro-containers or managed Compute Engine instances. Both require anapp.yamlfile to describe the app to the service. While the app_could_still serve all it’s static files itself, one of the benefits of the platform is having Google’s infrastructure handle that for us so it can be offloaded and the app only has to deal with dynamic requests. The platform also handles logging and http gzip compression so these can be removed from the codebase as well.

The yaml file also contains other options to control instance size and auto-scaling so for true deployment freedom you would likely have separateapp-classic.yamlandapp-vm.yamlfiles and this can help when making the transition from AppEngine Classic to Managed VMs.

app-engine.yaml

✖ Error 404 while fetching file: Not Found

Router configuration

We’ll now use thebuild constraintsagain like we did when creating ourapp-standalone.goinstance but this time with the opposite tags to use this file_if_the build has the appengine or appenginevm tags (added automatically when deploying to these platforms).

This allows us to replace thecreateMux()function to create our Echo server_without_any of the static file handling and logging + gzip middleware which is no longer required. Also worth nothing is that GAE classic provides a wrapper to handle serving the app so instead of amain()function where we run the server, we instead wire up the router to the defaulthttp.Handlerinstead.

app-engine.go

✖ Error 404 while fetching file: Not Found

Managed VMs are slightly different. They are expected to respond to requests on port 8080 as well as special health-check requests used by the service to detect if an instance is still running in order to provide automated failover and instance replacement. Thegoogle.golang.org/appenginepackage provides this for us so we have a slightly different version for Managed VMs:

app-managed.go

✖ Error 404 while fetching file: Not Found

So now we have three different configurations. We can build and run our app as normal so it can be executed locally, on a full Compute Engine instance or any other traditional hosting provider (including EC2, Docker etc…). This build will ignore the code in appengine and appenginevm tagged files and theapp.yamlfile is meaningless to anything other than the AppEngine platform.

We can also run locally using theGoogle AppEngine SDK for Goeither emulatingAppEngine Classic:

goapp serve

OrManaged VMs:

gcloud config set project [your project id]
gcloud preview app run .

And of course we can deploy our app to both of these platforms for easy and inexpensive auto-scaling joy.

Depending on what your app actually does it’s possible you may need to make other changes to allow switching between AppEngine provided service such as Datastore and alternative storage implementations such as MongoDB. A combination of go interfaces and build constraints can make this fairly straightforward but is outside the scope of this recipe.

Maintainers

Source Code

results matching ""

    No results matching ""